package main

// package options

import (
	"fmt"
	"time"
)

// 定义了一个连接类型
type Connection struct {
	addr    string
	cache   bool
	timeout time.Duration
}

// 这个常量就是默认的值，如果没有传递参数，就使用这里的值充当默认值
const (
	defaultTimeout = 10
	defaultCaching = false
)

// 就是负责输入默认值的底层对象——这个是隐藏的对象options
// 有哪些参数是需要设置默认值的就在这里定义
type options struct {
	timeout time.Duration
	caching bool
}

// Option overrides behavior of Connect.
// 问题来了，就是这里定义成一个接口要干什么？（应该就是为了接收参数，这个参数实现了接口，因为你不知道会传一个什么参数进来吧）
// 定义的是一个接口类型——这个类型要么就是作为参数要么就是
type Option interface {
	apply(*options)
}

// 这里定义了一个函数类型——optionFunc
type optionFunc func(*options)

// 函数类型定义了一个方法，函数类型的方法——这个方法实现了接口——使得optionFunc这个函数类型可以作为参数（Option）参与后续操作
// 而这个函数方法的函数体就是执行了函数本身（这里的功能就是将默认值的数据赋值给对象属性），
// 下面应用这个内容就是定义成了匿名函数返回，而执行apply函数本身就是在执行匿名函数(optionFunc)中的逻辑
// 这个函数类型实现了apply的方法，就是实现了Option的接口，接下来有Option参数的地方就可以使用这个函数类型代替
// 而这个参数也不是普通的值参数，它还是有自己的行为的，可以执行某些动作
func (f optionFunc) apply(o *options) {
	f(o)
}

// 比如就是下面的WithTimeout函数，返回值是一个接口类型Option（也可以是实现这个接口的类型——例如optionFunc，已经实现了接口），
// 顺着optionfunc的架子往下写就可以了，这下面主体是匿名函数，然后匿名函数之前的optionFunc应该是一种类型转换
// 这样下去，这个函数既返回了optionFunc类型，而这个类型实现了接口方法（就是调用本身函数这个动作），又将逻辑写入到类型中（具体类型）
func WithTimeout(t time.Duration) Option {
	return optionFunc(func(o *options) {
		o.timeout = t
	})
	// 所以这个返回，返回的其实是一个函数，optionFunc()就是类型转化，转化完表示返回的函数已经实现了option接口，可以作为相应参数传递
}

func WithCaching(cache bool) Option {
	return optionFunc(func(o *options) {
		o.caching = cache
	})
}

// Connect creates a connection.
func NewConnect(addr string, opts ...Option) (*Connection, error) {
	// 下面先是定义了基本类型——先设置为默认的情况
	options := options{
		timeout: defaultTimeout,
		caching: defaultCaching,
	}
	// 这个循环会迭代opt函数中几个Option.在目前的代码逻辑中应该就是optionFunc，迭代执行optionFunc方法（WithTimeout\WithCaching）
	// 这个位置这里传进来的都是函数类型，而且还是自带方法的函数，执行了apply就是把相应的函数体执行了一遍
	for _, o := range opts {
		o.apply(&options)
	}

	return &Connection{
		addr:    addr,
		cache:   options.caching,
		timeout: options.timeout,
	}, nil
}

func main() {
	s := "try"
	c, _ := NewConnect(s)
	fmt.Println("这个就是没有参数的", c.cache)
	t := WithCaching(true)
	c, _ = NewConnect(s, t)
	fmt.Println("有一个参数的", c.cache)
}
